/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.ui.widgets;

import java.util.Calendar;
import java.util.Date;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

import com.aphrodite.ui.graphics.GraphicUtil;
import com.aphrodite.util.BitmapFont;
import com.aphrodite.util.DateTimeUtil;

//Must implement traverse
/**
 * Date Picker Widget.
 * <p>
 * The properties of the calendar are fully open for customization<br/> <img
 * src="../../../resources/aphro_calendar.png">
 * </p>
 * 
 * @author kentelt
 * 
 */
public final class DatePicker extends TraversableWidget {
	/* cells border properties */
	private int borderWidth = 4;
	private Calendar calendar = null;
	private int cellHeight = 0;

	private int cellWidth = 0;

	/* internal time properties */
	private long currentTimestamp = 0;

	/* header (month-year label) properties */
	private Font headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL);
	private int headerFontColor = 0;// 0xffffff;

	/* internal properties */
	private int headerHeight = 0;

	/* elements padding */
	private int padding = 1;

	private int selAlphaLvl = 180;
	/* cells properties */
	// private Font font = Font.getDefaultFont();
	private int selectedBgColor = 0xccccff;
	private int selectedForeColor = 0xffffff;
	/* starting week day: 0 for monday, 6 for sunday */
	private int startWeekday = 0;

	/* weekday labels properties */
	// private int weekdayBgColor = 0x7F0000; // 0xccccff;
	private int weekdayFontColor = 0xffffff;// 0x0000ff;

	private int weekHeight = 0;

	private int weeks = 0;

	public DatePicker(Theme theme) {
		this(theme, new Date());
	}

	/**
	 * @param theme
	 *            to be used by the widget
	 * @param date
	 *            to set calendar to
	 */
	public DatePicker(Theme theme, Date date) {
		super(theme, 0, 0);
		calendar = Calendar.getInstance();
		setDate(date);
		updateModel();
	}

	public void cleanUp() {
		// MONTH_LABELS = null;
		// WEEKDAY_LABELS = null;
		headerFont = null;
		// widgetFont = null;
	}

	public final Date getSelectedDate() {
		return calendar.getTime();
	}

	private int getStartWeekday() {
		// let's create a new calendar with same month and year, but with day 1
		Calendar c = Calendar.getInstance();
		c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
		c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
		c.set(Calendar.DAY_OF_MONTH, 1);

		// we must normalize DAY_OF_WEEK returned value
		return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
	}

	/**
	 * Adjust the height of the widget according to the current calendar view
	 */
	private void initHeight() {
		height = (headerHeight + weekHeight + this.weeks * (cellHeight + borderWidth) + borderWidth);
	}

	public boolean moveDown() {
		scroll(7);
		reDraw();
		return true;
	}

	public boolean moveLeft() {
		scroll(-1);
		reDraw();
		return true;
	}

	public boolean moveRight() {
		scroll(1);
		reDraw();
		return true;
	}

	public boolean moveUp() {
		scroll(-7);
		reDraw();
		return true;
	}

	public void render(Graphics g) {
		if (!isVisible)
			return;

		capture(g, width, height, true);
		g.setColor(headerFontColor);
		if (theme.bmFont != null) {
			theme.bmFont.getFont(BitmapFont.STYLE_BOLD).drawString(g,
					DateTimeUtil.getMonth(calendar.get(Calendar.MONTH)) + " " + calendar.get(Calendar.YEAR), width / 2,
					padding, Graphics.TOP | Graphics.HCENTER);
		} else {
			g.setFont(headerFont);
			g.drawString(DateTimeUtil.getMonth(calendar.get(Calendar.MONTH)) + " " + calendar.get(Calendar.YEAR),
					width / 2, padding, Graphics.TOP | Graphics.HCENTER);
		}

		// painting week days labels
		g.translate(0, headerHeight);

		g.setColor(theme.highLightColor);
		g.fillRect(0, 0, width, weekHeight);

		g.setColor(weekdayFontColor);
		g.setFont(theme.widgetFont);
		for (int i = 0; i < 7; i++) {
			if (theme.bmFont != null) {
				theme.bmFont.drawString(g, DateTimeUtil.getWeekday((i + startWeekday) % 7), (borderWidth + i
						* (cellWidth + borderWidth) + cellWidth / 2), padding, Graphics.TOP | Graphics.HCENTER);
			} else {
				g.drawString(DateTimeUtil.getWeekday((i + startWeekday) % 7),
						(borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2), padding, Graphics.TOP
								| Graphics.HCENTER);
			}
		}

		// painting cells borders
		// g.translate(0, weekHeight);
		g.translate(0, weekHeight);

		g.setColor(theme.borderColor);
		for (int i = 0; i <= weeks; i++) {
			g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);/*
																			 * ===
																			 * horizontal
																			 * borders
																			 */
		}
		for (int i = 0; i <= 7; i++) {
			g.fillRect((i * (cellWidth + borderWidth)), 0, borderWidth, height - headerHeight - weekHeight);/*
																											 * ||
																											 * vertical
																											 * border
																											 */
		}

		// painting days
		int days = DateTimeUtil.getDaysInMonth(calendar);// getMonthDays();
		int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;

		g.setColor(theme.fgColor);

		int currentDay = calendar.get(Calendar.DAY_OF_MONTH);

		for (int i = 0; i < days; i++) {
			int weekday = (dayIndex + i) % 7;
			int row = (dayIndex + i) / 7;

			int x1 = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
			int y1 = borderWidth + row * (cellHeight + borderWidth) + padding;

			// if this is the current day, we'll use selected bg and fore
			// colors
			if (i + 1 == currentDay) {
				g.setColor(selectedBgColor);
				// g.fillRect();
				GraphicUtil.fillRect(g, selAlphaLvl, (borderWidth + weekday * (cellWidth + borderWidth)),
						(borderWidth + row * (cellHeight + borderWidth)), cellWidth, cellHeight);
				// g.fillRect((borderWidth + weekday * (cellWidth +
				// borderWidth)), (borderWidth + row
				// * (cellHeight + borderWidth)), cellWidth, cellHeight);
				g.setColor(selectedForeColor);
			}

			if (theme.bmFont != null) {
				theme.bmFont.drawString(g, "" + (i + 1), x1, y1, Graphics.TOP | Graphics.HCENTER);
			} else {
				g.drawString("" + (i + 1), x1, y1, Graphics.TOP | Graphics.HCENTER);
			}

			// if this is the current day, we must restore standard fore
			// color
			if (i + 1 == currentDay) {
				g.setColor(theme.fgColor);
			}
		}
		// let's translate back!
		g.translate(0, -headerHeight - weekHeight);
		// g.translate(0, -titleHeight);
		release(g, width, height, true);

	}

	private void scroll(int delta) {
		int prevMonth = calendar.get(Calendar.MONTH);

		setDate(currentTimestamp + 86400000 * delta);

		// we have to check if month has changed
		// if yes, we have to recalculate month height
		// since weeks number could be changed
		if (calendar.get(Calendar.MONTH) != prevMonth) {
			initHeight();
		}
	}

	public void setBorderWidth(int borderWidth) {
		this.borderWidth = borderWidth;
	}

	public void setDate(Date d) {
		currentTimestamp = d.getTime();
		calendar.setTime(d);
		// weeks number can change, depending on week starting day and month
		// total days
		this.weeks = (int) Math.ceil(((double) getStartWeekday() + DateTimeUtil.getDaysInMonth(calendar)) / 7);
		reDrawWidget();
	}

	public void setDate(long timestamp) {
		setDate(new Date(timestamp));
		stateChanged(this);
	}

	public void setHeaderFontColor(int headerFontColor) {
		this.headerFontColor = headerFontColor;
		stateChanged(this);
	}

	public void setSelectorBGColor(int selectedBgColor, int alphaLevel) {
		this.selectedBgColor = selectedBgColor;
		this.selAlphaLvl = alphaLevel;
		stateChanged(this);
	}

	public void setSelectorFGColor(int selectedForeColor) {
		this.selectedForeColor = selectedForeColor;
	}

	public void setWeekdayFontColor(int weekdayFontColor) {
		this.weekdayFontColor = weekdayFontColor;
	}

	protected final boolean traverse(int dir, int viewportWidth, int viewportHeight, int[] visRect_inout) {
		return false;
	}

	protected final void updateModel() {
		if (theme.bmFont != null) {
			// let's initialize calendar size
			this.cellWidth = theme.bmFont.stringWidth("MM") + 2 * padding;
			this.cellHeight = theme.bmFont.getHeight() + 2 * padding;

			this.headerHeight = theme.bmFont.getFont(BitmapFont.STYLE_BOLD).getHeight() + 2 * padding;
			this.weekHeight = theme.bmFont.getHeight() + 2 * padding;
		} else {
			// let's initialize calendar size
			this.cellWidth = theme.widgetFont.stringWidth("MM") + 2 * padding;
			this.cellHeight = theme.widgetFont.getHeight() + 2 * padding;

			this.headerHeight = headerFont.getHeight() + 2 * padding;
			this.weekHeight = theme.widgetFont.getHeight() + 2 * padding;
		}
		width = (7 * (cellWidth + borderWidth) + borderWidth);
		initHeight();

	}

}
